//
//  ObjLoader.cpp
//  TrackingSim
//
//  Created by Garrett Manka on 1/28/12.
//  Copyright (c) 2012. All rights reserved.
//

#include <fstream>
#include <graphics/ObjLoader.h>
#include <graphics/Object.h>
#include <iostream>
#include <cctype>

int ObjLoader::s_current_group_id = 0;

ObjLoader::ObjLoader()
{
    
}

ObjLoader::~ObjLoader()
{
    for(unsigned int i = 0 ; i < m_objects.size() ; ++i )
    {
        delete m_objects[i];
    }
}

void ObjLoader::load(const std::string& path)
{
    std::ifstream obj_file( path.c_str(), std::ifstream::in);
    
    if( obj_file.is_open() )
    {
        Object* obj = 0;
        int vertex_count = 0;
        int normal_count = 0;
        
        while( !obj_file.eof() )
        {
            char c = obj_file.get();
            
            switch ( c )
            {
                case '#':
                {
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                case 'o':
                {
                    obj = new Object();
                    
                    m_objects.push_back(obj);
                    obj_file.ignore(10, ' ');
                    std::string name;
                    obj_file >> name;
                    obj->setName(name);
                    obj_file.ignore(10, '\n');
                    break;
                }
                    
                case 'v':
                {
                    double x , y , z;

                    char type = obj_file.get();
                    
                    switch ( type )
                    {
                        case ' ':
                        {
                            ++vertex_count;
                            obj_file >> x >> y >> z;
                            obj->addVertex(Vertex(x,y,z), vertex_count);
                            break;
                        }
                            
                        case 'n':
                        {
                            ++normal_count;
                            obj_file >> x >> y >> z;
                            obj->addNormal(Normal(x,y,z), normal_count);
                            break;
                        }
                        
                        case 't':
                        {
                            obj_file >> x >> y;
                            break;
                        }
                         
                        default:
                        {
                            //obj_file.ignore( 1024, '\n' );
                            break;
                        }
                    }
                    
                    obj_file.ignore(10, '\n');
                    break;
                }
                    
                case 'f':
                {
                    Face f;

                    while( obj_file.peek() != '\n')
                    {
                        int vertex_index = 0;
                        int texture_index = 0;
                        int normal_index = 0;
                        
                        obj_file >> vertex_index;
                        
                        char nextChar = obj_file.peek();
                        
                        if( nextChar == ' ' )
                        {
                            f.addVertexIndex(vertex_index);
                            obj_file.ignore(1);
                            continue;
                        }
                        else if( nextChar == '/' )
                        {
                            obj_file.ignore(1);
                            
                            nextChar = obj_file.peek();
                            
                            if( isdigit( nextChar ) )
                            {
                                obj_file >> texture_index;
                                //add texture index
                            }
                            else
                            {
                                obj_file.ignore(1);
                                obj_file >> normal_index;
                            }
                            
                            f.addVertexIndex(vertex_index);
                            f.addNormalIndex(normal_index);
                        }
                    }
                    
                    //toss out the new line char
                    obj_file.ignore(1);
                    
                    obj->addFace(f);

                    break;
                }
                    
                case 'm':
                {
                    //use for material definitions later
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                case 's':
                {
                    //use for shading later
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                case 'u':
                {
                    //use for materials later
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                default:
                {
                    obj_file.ignore(1024, '\n');
                    break;
                }
            }
        }
    }
    
    //printLogFile();
}

void ObjLoader::load(const std::string& path, std::vector<Object*>* objects)
{
    std::ifstream obj_file( path.c_str(), std::ifstream::in);
    
    if( obj_file.is_open() )
    {
        ++s_current_group_id;
        
        Object* obj = 0;
        int vertex_count = 0;
        int normal_count = 0;
        
        while( !obj_file.eof() )
        {
            char c = obj_file.get();
            
            switch ( c )
            {
                case '#':
                {
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                case 'o':
                {
                    obj = new Object();
                    
                    objects->push_back(obj);
                    obj_file.ignore(10, ' ');
                    std::string name;
                    obj_file >> name;
                    obj->setName(name);
                    
                    int last_period_pos = path.find_last_of('.');
                    //int last_slash_pos = path.find_last_of("/\\");
                    
                    std::string group_name = path.substr(0,last_period_pos);
                    
                    obj->setMeshGroup( group_name );
                    
                    obj->setGroupID(s_current_group_id);
                    obj_file.ignore(10, '\n');
                    break;
                }
                    
                case 'v':
                {
                    double x , y , z;
                    
                    char type = obj_file.get();
                    
                    switch ( type )
                    {
                        case ' ':
                        {
                            ++vertex_count;
                            obj_file >> x >> y >> z;
                            obj->addVertex(Vertex(x,y,z), vertex_count);
                            break;
                        }
                            
                        case 'n':
                        {
                            ++normal_count;
                            obj_file >> x >> y >> z;
                            obj->addNormal(Normal(x,y,z), normal_count);
                            break;
                        }
                            
                        case 't':
                        {
                            obj_file >> x >> y;
                            break;
                        }
                            
                        default:
                        {
                            //obj_file.ignore( 1024, '\n' );
                            break;
                        }
                    }
                    
                    obj_file.ignore(10, '\n');
                    break;
                }
                    
                case 'f':
                {
                    Face f;
                    
                    while( obj_file.peek() != '\n')
                    {
                        int vertex_index = 0;
                        int texture_index = 0;
                        int normal_index = 0;
                        
                        obj_file >> vertex_index;
                        
                        char nextChar = obj_file.peek();
                        
                        if( nextChar == ' ' )
                        {
                            f.addVertexIndex(vertex_index);
                            obj_file.ignore(1);
                            continue;
                        }
                        else if( nextChar == '/' )
                        {
                            obj_file.ignore(1);
                            
                            nextChar = obj_file.peek();
                            
                            if( isdigit( nextChar ) )
                            {
                                obj_file >> texture_index;
                                //add texture index
                            }
                            else
                            {
                                obj_file.ignore(1);
                                obj_file >> normal_index;
                            }
                            
                            f.addVertexIndex(vertex_index);
                            f.addNormalIndex(normal_index);
                        }
                    }
                    
                    //toss out the new line char
                    obj_file.ignore(1);
                    
                    obj->addFace(f);
                    
                    break;
                }
                    
                case 'm':
                {
                    //use for material definitions later
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                case 's':
                {
                    //use for shading later
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                case 'u':
                {
                    //use for materials later
                    obj_file.ignore(1024, '\n');
                    break;
                }
                    
                default:
                {
                    obj_file.ignore(1024, '\n');
                    break;
                }
            }
        }
    }
}


void ObjLoader::printLogFile()
{
    std::ofstream out_log( "parsed_obj.txt", std::ofstream::out);
    
    if( out_log.is_open() )
    {
        for(unsigned int i = 0 ; i < m_objects.size() ; ++i )
        {
            Object* currObject = m_objects[i];
            
            out_log << currObject->getName() << std::endl;
            
            for (unsigned int j = 0; j < currObject->getVertexList().size(); ++j) 
            {
                Vertex v = currObject->getVertexList()[j];
                out_log<<"vertex["<<j<<"] x: "<<v.x()<<" y: "<<v.y()<<" z: "<<v.z()<<std::endl;
            }
            
            for (unsigned int j = 0; j < currObject->getNormalList().size(); ++j) 
            {
                Vertex v = currObject->getNormalList()[j];
                out_log<<"normal["<<j<<"] x: "<<v.x()<<" y: "<<v.y()<<" z: "<<v.z()<<std::endl;
            }
            
            for( unsigned int j = 0; j < currObject->getFaceList().size() ; ++j )
            {
                //For Each Vertex/Normal in Face
                const Face& f = currObject->getFaceList()[j];
                
                out_log<<"face["<<j<<"] ";
                
                for(unsigned int k = 0 ; k < f.getVertexIndexList().size() ; ++k )
                {
                    int normal_index = f.getNormalIndexList()[k];
                    int vertex_index = f.getVertexIndexList()[k];
                    
                    out_log<< vertex_index << "//" << normal_index << " ";
                }
                
                out_log<< std::endl;
            }
        }
        
        out_log.close();
    }
}

std::vector<Object*>& ObjLoader::getObjects()
{
    return m_objects;
}